package com.codi.bus.core.service.impl;

import com.codi.base.cache.CacheUtil;
import com.codi.base.exception.BaseAppException;
import com.codi.base.exception.ExceptionHandler;
import com.codi.base.util.*;
import com.codi.bus.constant.CacheKeyConst;
import com.codi.bus.constant.FundConst;
import com.codi.bus.constant.GlobalConstant;
import com.codi.bus.constant.RuleConstant;
import com.codi.bus.core.dao.LookupDao;
import com.codi.bus.core.dao.UserDao;
import com.codi.bus.core.domain.Lookup;
import com.codi.bus.core.domain.OrganProfile;
import com.codi.bus.core.domain.User;
import com.codi.bus.core.domain.UserBank;
import com.codi.bus.core.resultModel.*;
import com.codi.bus.core.service.*;
import com.codi.bus.core.service.model.*;
import com.codi.bus.core.service.organ.OrganProfileService;
import com.codi.bus.core.service.util.FundUtil;
import com.codi.bus.core.service.util.HundsunUtils;
import com.codi.bus.core.service.util.ModelPopulateUtils;
import com.codi.bus.exception.BaseException;
import com.codi.bus.exception.SystemDBException;
import com.codi.bus.util.LookupUtil;
import com.codi.fundData.dao.FundMMYieldPerformanceDao;
import com.codi.fundData.domain.FundMMYieldPerformance;
import com.codi.fundData.domain.SecurityProduct;
import com.codi.fundData.service.FundMMYieldPerformanceService;
import com.codi.message.constant.ErrorConst;
import com.codi.message.constant.ErrorConstant;
import com.codi.message.util.MessageUtil;
import com.codi.trade.dto.HsShareQueryDto;
import com.codi.trade.service.ShareQueryService;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 快速赎回 接口实现类
 *
 * @author song-jj
 */
@Service("walletService")
@SuppressWarnings("Duplicates")
public class WalletServiceImpl implements WalletService {

    private static final Logger logger = LoggerFactory.getLogger(WalletServiceImpl.class);
    /**
     * 最近七日的收益率
     **/
    private static final int WALLET_NET_VALUE_LIST_DAYS = 7;
    @Resource(name = "userDao")
    private UserDao userDao;
    @Resource(name = "lookupDao")
    private LookupDao lookupDao;
    @Resource(name = "fundDetailService")
    private FundDetailService fundDetailService;
    @Resource(name = "fundMMYieldPerformanceService")
    private FundMMYieldPerformanceService fundMMYieldPerformanceService;
    @Resource(name = "fundMMYieldPerformanceDao")
    private FundMMYieldPerformanceDao fundMMYieldPerformanceDao;
    @Resource(name = "userService")
    private UserService userService;
    @Resource(name = "queryService")
    private QueryService queryService;
    @Resource(name = "lookupService")
    private LookupService lookupService;
    @Resource(name = "shareQueryService")
    private ShareQueryService shareQueryService;
    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;
    @Autowired
    private RedisTemplate<String, String> redisTemplateStr;
    @Autowired
    private BankService bankService;
    @Resource
    private OrganProfileService organProfileService;

    @Override
    public WalletDetailResult getWalletDetail(String token, String deviceId) throws BaseAppException {
        logger.debug("token={},deviceId={}", token, deviceId);

        // 校验fundCode是否在T0列表中
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
        if (ListUtil.isEmpty(lookups)) {
            logger.error("wallet init sql error, please check.");
            ExceptionHandler.publish(ErrorConstant.ERROR_SYS_EXCEPTION);
        }

        WalletDetailResult result = new WalletDetailResult();

        String fundCode = null;
        // --------------- 和用户相关内容 begin ---------------
        // 是否有余额
        boolean hasBalance = false;
        User user = null;
        boolean isOrg = false;

        AssetFundModel wallet = null;
        if (StringUtil.isNotEmpty(token)) { // 有token情形；没有token时不需要要处理
            ValidateTokenResult validateTokenResult = userService.validateToken(token, deviceId);
            if (validateTokenResult.getSuccess()) {
                Long userId = validateTokenResult.getUserId();
                user = userService.getUser(userId);
                isOrg = user.getIsOrg();

                /**
                 * 统一用资产查询接口 - Shangdu Lin - 2017-01-19
                 */
                AssetsResult assetsResult = queryService.queryWalletAssets(userId);
                if (!assetsResult.getSuccess()) {
                    String errorMsg = MessageUtil.getErrorMessage(assetsResult.getErrorCode(), null);
                    logger.warn("token={},errorMsg={}", token, errorMsg);
                } else {

                    wallet = assetsResult.getWallet();
                    if (wallet != null) {
                        // 零钱
                        result.setWalletMoney(MoneyUtils.formatMoney(wallet.getTotalWorthValue(), 2));
                        // 总当日收益
                        result.setTodayIncome(MoneyUtils.formatMoney(wallet.getTotalTodayIncome(), 2));

                        if (wallet.getTotalWorthValue() != null) {
                            if (wallet.getTotalWorthValue().compareTo(GlobalConstant.BIG_DECIMAL_0) > 0
                                && wallet.getTotalWorthValue().compareTo(wallet.getInTransitAmount()) > 0) {
                                hasBalance = true;
                            }
                        }

                        fundCode = wallet.getFundCode();
                    }
                }
            } else {
                String errorMsg = MessageUtil.getErrorMessage(validateTokenResult.getErrorCode(), null);
                logger.warn("token is invalid, token={},errorMsg={}", token, errorMsg);
            }
        }
        // --------------- 和用户相关内容 end ---------------
        Lookup hitLookup = null;
        if (StringUtil.isEmpty(fundCode)) {
            for (Lookup lookup : lookups) {
                // =1 当前在用的T0
                if (EqualsUtil.equals(1, lookup.getRuleStatus().intValue())) {
                    logger.info("fundCode={} in T0 List", lookup.getRuleValue());
                    hitLookup = lookup;
                    break;
                }
            }

            if (hitLookup == null) {
                logger.error("There is no T0 Fund, please check!");
                ExceptionHandler.publish(ErrorConst.WALLET_INVALID_FUND_CODE);
            }

            fundCode = hitLookup.getRuleValue();
        } else {
            for (Lookup lookup : lookups) {
                // =1 当前在用的T0
                if (EqualsUtil.equals(fundCode, lookup.getRuleValue())) {
                    logger.info("fundCode={} in T0 List", lookup.getRuleValue());
                    hitLookup = lookup;
                    break;
                }
            }

            if (hitLookup == null) {
                logger.error("There is no T0 Fund, please check!");
                ExceptionHandler.publish(ErrorConst.WALLET_INVALID_FUND_CODE);
            }
        }

        // ------- 非用户相关内容 begin -----------
        SecurityProduct product = fundDetailService.getSecurityProduct(fundCode);
        if (product == null) {
            logger.error("Security Product is not exist");
            result.setErrorCode(ErrorConstant.ERROR_FUND_NOT_EXIST);
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
            result.setSuccess(false);
            return result;
        }

        result.setFundCode(fundCode);
        result.setFundName(product.getChiName());
        result.setFundNameAbbr(product.getSecuAbbr());

        // 每日最大限额
        //result.setMaxLimit(hitLookup.getComponentValue());

        // spy 2017-11-1
        String maxLimit = LookupUtil.getT0FieldValueStr(hitLookup.getComponentValue(), isOrg, RuleConstant.FIELD_DAY_LIMIT);
        result.setMaxLimit(maxLimit);


        // 七日年化率
        FundMMYieldPerformance perf = fundMMYieldPerformanceService.getFundMMYieldPerformance(product.getInnerCode());
        result.setLatestWeeklyYield(MoneyUtils.formatMoney(perf.getLatestWeeklyYield(), 2));// 最近7日折算年收益率（%）
        result.setTradingDay(perf.getTradingDay());

        // 最近七天的七日年化收益率(降序)
        setWeekYield(product, result);


        // ------- 非用户相关内容 end -------------

        // 查询在售的零钱包
        Lookup lookupOnSale = null;
        for (Lookup lookup : lookups) {
            // =1 当前在用的T0
            if (EqualsUtil.equals(1, lookup.getRuleStatus().intValue())) {
                logger.info("fundCode={} in T0 List", lookup.getRuleValue());
                lookupOnSale = lookup;
                break;
            }
        }
        if (lookupOnSale != null) {
            if (EqualsUtil.equals(lookupOnSale.getRuleValue(), fundCode)) {
                result.setFundCodeOnSale(result.getFundCode());
                result.setFundNameOnSale(result.getFundName());
                result.setFundNameAbbrOnSale(result.getFundNameAbbr());
            } else {
                SecurityProduct productOnSale = fundDetailService.getSecurityProduct(lookupOnSale.getRuleValue());
                if (productOnSale == null) {
                    logger.error("Security Product is not exist");
                    result.setErrorCode(ErrorConstant.ERROR_FUND_NOT_EXIST);
                    result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
                    result.setSuccess(false);
                    return result;
                }

                result.setFundCodeOnSale(lookupOnSale.getRuleValue());
                result.setFundNameOnSale(productOnSale.getChiName());
                result.setFundNameAbbrOnSale(productOnSale.getSecuAbbr());
            }
        }

        // Shangdu Lin - 20170502 - 设置基金状态
        FundModel fundModel = queryService.queryFund(result.getFundCodeOnSale());
        //设置是否能买卖
        setSellAndBuy(hasBalance, result.getFundCodeOnSale(), wallet, result);


        // Shangdu Lin - 20170330 - 获取交易限制
        // @Deprecated 2017-10-12 11:13:24 spy
        // result.setLimits(queryService.queryTradeLimits(result.getFundCodeOnSale()));

        //2017-10-12 11:08:14 spy
        String custType = GlobalConstant.CUST_TYPE_PERSONAL;
        String capitalMode = null;
        if (user != null) {
            if (user.getIsOrg()) {
                custType = GlobalConstant.CUST_TYPE_ORGANIZATION;
                capitalMode = GlobalConstant.CAPTIAL_MODE_K;
            }
        }
        result.setLimits(queryService.queryTradeLimits(result.getFundCodeOnSale(), capitalMode, null, custType));

        result.setSuccess(true);
        return result;
    }

    @Override
    public WalletDetailResultV2 getWalletDetailV2(Long userId) throws BaseAppException {
        // 校验fundCode是否在T0列表中
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
        if (ListUtil.isEmpty(lookups)) {
            logger.error("wallet init sql error, please check.");
            ExceptionHandler.publish(ErrorConstant.ERROR_SYS_EXCEPTION);
        }
        WalletDetailResultV2 result = new WalletDetailResultV2();

        // --------------- 和用户相关内容 begin ---------------

        AssetsResult assetsResult = queryService.queryWalletAssets(userId);
        if (!assetsResult.getSuccess()) {
            String errorMsg = MessageUtil.getErrorMessage(assetsResult.getErrorCode(), null);
        } else {
            AssetFundModel wallet = assetsResult.getWallet();
            if (wallet != null) {
                // 零钱
                result.setTotalWorthValue(MoneyUtils.formatMoney(wallet.getTotalWorthValue(), 2));
                // 总当日收益
                result.setTotalTodayIncome(MoneyUtils.formatMoney(wallet.getTotalTodayIncome(), 2));
                //设置总收益
                result.setTotalIncome(MoneyUtils.formatMoney(wallet.getTotalAccumIncome(), 2));
                //设置总在途
                result.setInTransitAmount(wallet.getInTransitAmount());
                //在途笔数
                result.setInTransitNum(wallet.getInTransitNum());
                //设置净值日期
                result.setNetValueDate(wallet.getNetValueDate());
                //设置基金详情的基金姓名
                setFundName(wallet, lookups);
                result.setFundModelList(wallet.getFundModelList());
            }
        }
        result.setSuccess(true);
        return result;
    }

    @Override
    public void setFundName(AssetFundModel wallet, List<Lookup> lookups) {
        List<AssetFundModel> fundList = wallet.getFundModelList();
        if (fundList != null) {
            for (AssetFundModel m : fundList) {
                String fundCode = m.getFundCode();
                for (Lookup lookup : lookups) {
                    if (lookup.getRuleValue().equals(fundCode)) {
                        m.setFundName(lookup.getRuleText());
                        break;
                    }
                }
            }
        }
    }

    /**
     * author:wangzhenhao
     */
    @Override
    public CashChangeListResult getWalletCashChangeDetails(Long userId) {
        // T0 Funds 基金
        List<Lookup> lookups = lookupService.getLookups(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
        ShareListResult shareListResult = null;
        CashChangeListResult cashChangeListResult = new CashChangeListResult();
        List<CashChangeResult> cashChangeResults = new ArrayList<>();
        Map<String, TradeLimitModel> tradeLimitModels = new HashMap<>();

        Preconditions.checkNotNull(lookups, "T0数据为空请检查");

        User user = userService.getUser(userId);

        String custType = user.getIsOrg() ? GlobalConstant.CUST_TYPE_ORGANIZATION : GlobalConstant.CUST_TYPE_PERSONAL;


        for (Lookup lookup : lookups) {

            // 老接口只处理宝盈 spy 2017-10-30 17:49:48
            if (EqualsUtil.equals(GlobalConstant.T0_FUND_BAOYING_A, lookup.getRuleValue())
                || EqualsUtil.equals(GlobalConstant.T0_FUND_BAOYING_B, lookup.getRuleValue())) {

                shareListResult = queryShareModels(userId, lookup.getRuleValue());

                if (!shareListResult.getSuccess()) {
                    cashChangeListResult.setSuccess(false);
                    cashChangeListResult.setErrorCode(shareListResult.getErrorCode());
                    cashChangeListResult.setErrorType(shareListResult.getErrorType());

                    return cashChangeListResult;
                }

                List<ShareModel> models = shareListResult.getShares();

                for (ShareModel model : models) {
                    CashChangeResult cashChangeResult = new CashChangeResult();
                    cashChangeResult.setBankName(model.getBankName());
                    cashChangeResult.setBankAccount(model.getBankAccount());
                    cashChangeResult.setEnableShare(model.getEnableShares());
                    cashChangeResult.setTradeAccount(model.getTradeAccount());
                    cashChangeResult.setCapitalMode(model.getCapitalMode());
                    cashChangeResult.setFundCode(lookup.getRuleValue());

                    TradeLimitModel tradeLimitModel = null;
                    /**
                     * 赎回限额
                     */
                    String fastSellKey = lookup.getRuleValue() + model.getShareType() + model.getCapitalMode()
                        + GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL;
                    if (tradeLimitModels.get(fastSellKey) == null) {
                        tradeLimitModel = queryService.getTradeLimit(lookup.getRuleValue(),
                            GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL, model.getShareType(),
                            custType, model.getCapitalMode());
                        tradeLimitModels.put(fastSellKey, tradeLimitModel);
                    } else {
                        tradeLimitModel = tradeLimitModels.get(fastSellKey);
                    }

                    if (tradeLimitModel != null) {
                        cashChangeResult.setMinHoldShares(tradeLimitModel.getMinHoldShares());
                        cashChangeResult.setMinValue(tradeLimitModel.getMinValue());
                        cashChangeResult.setMaxValueForSell(tradeLimitModel.getMaxValue());
                    } else {
                        // 如果未找到，则不返回，前端根据是否有返回来决定是否做验证限制
                        logger.warn("sell tradeLimitModel does not exist");
                    }
                    // 设置最高可快速赎回的金额
                    // 如果为空，则不返回，前端根据是否有返回来决定是否做验证限制
                    if (!StringUtil.isEmpty(lookup.getComponentValue())) {
                        if (!StringUtil.isEmpty(lookup.getComponentValue())) {
                            String str = lookup.getComponentValue();
                            String maxLimit = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_DAY_LIMIT);

                            // 如果转换出错，则默认为最大值，让交易成功提交，由直销系统做最后的限制
                            cashChangeResult.setMaxValueForFastSell(BigDecimalUtil
                                .ConvertToBigDecimal(maxLimit, new BigDecimal(Integer.MAX_VALUE)));
                        }
                    }

                    cashChangeResults.add(cashChangeResult);
                }
                cashChangeListResult.setCashChangeResults(cashChangeResults);
            }
        }
        return cashChangeListResult;
    }

    @Override
    public CashChangeListResult getWalletCashChangeDetails(Long userId, String fundCode) {
        logger.info("userId={},fundCode={}", userId, fundCode);

        // T0 Funds 基金
        List<Lookup> lookups = lookupService.getLookups(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
        ShareListResult shareListResult = null;
        CashChangeListResult cashChangeListResult = new CashChangeListResult();
        List<CashChangeResult> cashChangeResults = new ArrayList<>();
        Map<String, TradeLimitModel> tradeLimitModels = new HashMap<>();

        Preconditions.checkNotNull(lookups, "T0数据为空请检查");

        User user = userService.getUser(userId);

        String custType = user.getIsOrg() ? GlobalConstant.CUST_TYPE_ORGANIZATION : GlobalConstant.CUST_TYPE_PERSONAL;

        //List<Lookup> epayLookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_EPAYBANK);

        String brandsType = FundUtil.getT0FundBrandsType(fundCode);

        for (Lookup lookup : lookups) {
            if (EqualsUtil.equals(lookup.getRuleValue(), fundCode.trim())) {

                shareListResult = queryShareModels(userId, lookup.getRuleValue());

                if (false == shareListResult.getSuccess()) {
                    cashChangeListResult.setSuccess(false);
                    cashChangeListResult.setErrorCode(shareListResult.getErrorCode());
                    cashChangeListResult.setErrorType(shareListResult.getErrorType());

                    return cashChangeListResult;
                }

                List<ShareModel> models = shareListResult.getShares();

                logger.info("models.size={}", models.size());
                for (ShareModel model : models) {
                    if (model.getCurrentShare().compareTo(BigDecimal.valueOf(0)) == 0) {
                        continue;
                    }
                    CashChangeResult cashChangeResult = new CashChangeResult();
                    List<UserBank> userBanks = bankService.getBanks(user.getUserId(), model.getBankAccount());
                    UserBank userBank = new UserBank();
                    if (userBanks != null && !userBanks.isEmpty()) {
                        userBank = userBanks.get(0);
                        cashChangeResult.setBranchBank(userBank.getBranchbank() == null ? "" : userBank.getBranchbank());
                        cashChangeResult.setBranchBankName(userBank.getBankBranchName() == null ? "" : userBank.getBankBranchName());
                        cashChangeResult.setBankCode(userBank.getBankCode());
                    }
                    cashChangeResult.setBankName(model.getBankName());
                    cashChangeResult.setBankAccount(model.getBankAccount());
                    cashChangeResult.setEnableShare(model.getEnableShares());
                    cashChangeResult.setTradeAccount(model.getTradeAccount());
                    cashChangeResult.setCapitalMode(model.getCapitalMode());
                    cashChangeResult.setFundCode(lookup.getRuleValue());
//                    for (Lookup epayLookup : epayLookups) {
//
//                        if (model.getBankName().equals(epayLookup.getRuleText())) {
//                            cashChangeResult.setBankCode(epayLookup.getRuleValue());
//                            break;
//                        }
//                    }

                    TradeLimitModel tradeLimitModel = null;
                    /**
                     * 赎回限额
                     */
                    String fastSellKey = lookup.getRuleValue() + model.getShareType() + model.getCapitalMode()
                        + GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL;
                    if (tradeLimitModels.get(fastSellKey) == null) {
                        tradeLimitModel = queryService.getTradeLimit(lookup.getRuleValue(),
                            GlobalConstant.FUND_BUSIN_CODE_APPLY_SELL, model.getShareType(),
                            custType,
                            model.getCapitalMode());

                        tradeLimitModels.put(fastSellKey, tradeLimitModel);
                    } else {
                        tradeLimitModel = tradeLimitModels.get(fastSellKey);
                    }

                    if (tradeLimitModel != null) {
                        cashChangeResult.setMinHoldShares(tradeLimitModel.getMinHoldShares());
                        cashChangeResult.setMinValue(tradeLimitModel.getMinValue());
                        cashChangeResult.setMaxValueForSell(tradeLimitModel.getMaxValue());
                    } else {
                        // 如果未找到，则不返回，前端根据是否有返回来决定是否做验证限制
                        logger.warn("sell tradeLimitModel does not exist");
                    }

                    // 设置最高可快速赎回的金额
                    // 如果为空，则不返回，前端根据是否有返回来决定是否做验证限制
                    if (!StringUtil.isEmpty(lookup.getComponentValue())) {
                        String str = lookup.getComponentValue();

                        String maxLimit = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_DAY_LIMIT);

                        // 如果转换出错，则默认为最大值，让交易成功提交，由直销系统做最后的限制
                        cashChangeResult.setMaxValueForFastSell(BigDecimalUtil
                            .ConvertToBigDecimal(maxLimit, new BigDecimal(Integer.MAX_VALUE)));
                    }


                    //TODO 这里结构设计有问题,先保持原先结构  spy 2017-11-1


                    String str = lookup.getComponentValue();
                    if (FundUtil.isBaoYingT0AB(fundCode)) {
                        Lookup tmpLookup = lookupService.getLookup(RuleConstant.T0FUND_BAOYING_CONFIG);
                        str = tmpLookup.getComponentValue();
                    }

                    if (StringUtil.isNotEmpty(str) && LookupUtil.isJSONObject(str)) {

                        String dayDealLimitStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_DAY_LIMIT);
                        String singleDealLimitStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_SINGLE_DEAL_LIMIT);
                        String totalCountStr = LookupUtil.getT0FieldValueStr(str, user.getIsOrg(), RuleConstant.FIELD_DAY_DEAL_COUNT_LIMIT);

                        // 单日交易限制
                        BigDecimal dayDealMaxValue = BigDecimalUtil.ConvertToBigDecimal(dayDealLimitStr);
                        cashChangeResult.setFastRedeemDayDealMaxValue(BigDecimalUtil.ConvertToBigDecimal(dayDealLimitStr));

                        // 剩余单日额度 spy 2017-11-3 10:56:05
                        if (EqualsUtil.equals("-1", dayDealLimitStr)) {
                        } else {
                            BigDecimal usedMoney = getFastRedeemDayUsedMoney(userId, brandsType);
                            BigDecimal finalValue = MoneyUtils.max(dayDealMaxValue.subtract(usedMoney), BigDecimal.ZERO);
                            cashChangeResult.setFastRedeemRetainDayDealMaxValue(finalValue);
                        }

                        // 单笔交易限额
                        cashChangeResult.setFastRedeemSingleDealMaxValue(BigDecimalUtil.ConvertToBigDecimal(singleDealLimitStr));

                        // 可交易总次数
                        int totalCount = Integer.valueOf(totalCountStr);
                        cashChangeResult.setFastRedeemTotalCountPerDay(Integer.valueOf(totalCountStr));

                        // 可交易剩余次数(总次数-已用次数)
                        if (EqualsUtil.equals("-1", totalCountStr)) {
                        } else {
                            int count = Math.max(totalCount - getFastRedeemCountInCurrentDay(userId, brandsType), 0);
                            cashChangeResult.setFastRedeemRetainCountPerDay(count);
                        }
                        switch (cashChangeResult.getFundCode()) {
                            case GlobalConstant.T0_FUND_JINYING_A:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("未结转收益于每月15日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_JINYING_B:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("未结转收益于每月15日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_CHANGCHENG_A:
                                cashChangeResult.setFastRedeemTime("快速取现时间为：每个工作日9:00-16:45");
                                cashChangeResult.setWalletString("未结转收益于每月15日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_CHANGCHENG_B:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("未结转收益于每月15日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_BAOYING_A:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("未结转收益于每月5日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_BAOYING_B:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("未结转收益于每月5日结转，结转后可赎回");
                                break;
                            case GlobalConstant.T0_FUND_JINYING2_A:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("每日结转当期收益");
                                break;
                            case GlobalConstant.T0_FUND_JINYING2_B:
                                cashChangeResult.setFastRedeemTime("");
                                cashChangeResult.setWalletString("每日结转当期收益");
                                break;
                            case GlobalConstant.T0_FUND_CHANGCHENG_E:
                                cashChangeResult.setFastRedeemTime("快速取现时间为：每个工作日9:00-16:45");
                                cashChangeResult.setWalletString("每日结转当期收益");
                                break;
                            default:
                                break;
                        }
                    }

                    cashChangeResults.add(cashChangeResult);
                }
                cashChangeListResult.setCashChangeResults(cashChangeResults);
            }
        }
        return cashChangeListResult;
    }

    @Override
    public ShareListResult queryShareModels(Long userId, String fundCode) {
        logger.info("queryShareModels - userId={}", userId);

        ShareListResult result = new ShareListResult();

        try {
            // Step 1: 获取用户
            User user = userDao.getEntity(userId);

            // 判断是否已开户
            if (user == null || StringUtil.isEmpty(user.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

            // Step 2: 资产查询, 查询该用户下的所有份额
            Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "1", "",
                fundCode, "", user.getExternalClientId(), "", "", null);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);

            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, lookups, result);
            result.setShares(shares);

            return result;

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryShareModels - Exception:", exception);
            throw new SystemDBException("queryShareModels Failure!", exception);
        }
    }

    @Override
    public ShareListResult queryOrganShareModels(Long profileId, String fundCode) {
        logger.info("queryShareModels - profileId={}", profileId);

        ShareListResult result = new ShareListResult();

        try {
            // Step 1: 获取机构
            OrganProfile organProfile = organProfileService.getById(profileId);

            // 判断是否已开户
            if (organProfile == null || StringUtil.isEmpty(organProfile.getExternalClientId())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
                return result;
            }

            // 根据恒生银行编号获取银行名称
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

            // Step 2: 资产查询, 查询该用户下的所有份额
            Map<String, Object> map = shareQueryService.query(GlobalConstant.MAX_REQUEST_NUM, 1, 1, "", "1", "",
                fundCode, "", organProfile.getExternalClientId(), "", "", null);

            // 查询结果
            List<Map<String, Object>> list = new ArrayList<>();
            HundsunUtils.ConvertToList(map, list, result);

            if (!result.getSuccess()) {
                return result;
            }

            // populate
            List<ShareModel> shares = ModelPopulateUtils.populateShare(list, lookups, result);
            result.setShares(shares);

            return result;

        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "queryShareModels - Exception:", exception);
            throw new SystemDBException("queryShareModels Failure!", exception);
        }
    }

    @Override
    public ShareListResult queryShareModels(String tradeAcco, String fundCode) {
        logger.info("query share models, tradeAcco={},fundCode={}", tradeAcco, fundCode);
        ShareListResult result = new ShareListResult();

        // 根据恒生银行编号获取银行名称
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

        //  资产查询, 查询该用户下的所有份额
        HsShareQueryDto shareQueryDto = HsShareQueryDto.builder()
            .request_num(GlobalConstant.MAX_REQUEST_NUM)
            .trade_acco(tradeAcco)
            .fund_code(fundCode)
            .build();

        Map<String, Object> map = shareQueryService.query(shareQueryDto);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);

        if (!result.getSuccess()) {
            return result;
        }

        // populate
        List<ShareModel> shares = ModelPopulateUtils.populateShare(list, lookups, result);
        result.setShares(shares);

        return result;
    }


    @Override
    public ShareListResult queryWalletShareModels(Long userId) {
        logger.info("query share models,userId={}", userId);

        User user = userDao.getEntity(userId);

        ShareListResult result = new ShareListResult();

        if (Strings.isNullOrEmpty(user.getExternalClientId())) {

            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_ACCOUNT);
            return result;
        }

        // 根据恒生银行编号获取银行名称
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);

        //  资产查询, 查询该用户下的所有份额
        HsShareQueryDto shareQueryDto = HsShareQueryDto.builder()
            .request_num(GlobalConstant.MAX_REQUEST_NUM)
            .client_id(user.getExternalClientId())
            .build();

        Map<String, Object> map = shareQueryService.query(shareQueryDto);

        // 查询结果
        List<Map<String, Object>> list = new ArrayList<>();
        HundsunUtils.ConvertToList(map, list, result);

        if (!result.getSuccess()) {
            return result;
        }

        // populate
        List<ShareModel> allShares = ModelPopulateUtils.populateShare(list, lookups, result);


        List<Lookup> walletFunds = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);

        Set<String> fundCodes = new HashSet<>(walletFunds.size());
        for (Lookup lookup : walletFunds) {
            fundCodes.add(lookup.getRuleValue());
        }

        List<ShareModel> shares = allShares.stream()
            .filter(shareModel -> fundCodes.contains(shareModel.getFundCode()))
            .collect(Collectors.toList());

        result.setShares(shares);
        result.setSuccess(true);

        return result;
    }

    @Override
    public WalletDetailResult getWalletDetailByFundCode(Long userId, String fundCode) throws BaseAppException {
        // 校验fundCode是否在T0列表中
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_T0FUND);
        if (ListUtil.isEmpty(lookups)) {
            logger.error("wallet init sql error, please check.");
            ExceptionHandler.publish(ErrorConstant.ERROR_SYS_EXCEPTION);
        }

        SecurityProduct product = fundDetailService.getSecurityProduct(fundCode);

        WalletDetailResult result = new WalletDetailResult();
        AssetsResult assetsResult = queryService.queryWalletAssets(userId);

        // --------------- 和用户相关内容 begin ---------------
        // 是否有余额
        boolean hasBalance = false;
        User user = userService.getUser(userId);

        //每日最高限额
        if (user != null) {
            setMaxLimit(result, lookups, fundCode, user.getIsOrg());

            //
            // 宝盈不支持机构快速赎回 // spy 2017-11-3 13:26:44
            if (FundUtil.isBaoYingT0AB(fundCode)) {
                if (user.getIsOrg()) {
                    result.setCanFastRedeem(false);
                }
            }
        }

        AssetFundModel wallet = null;
        if (!assetsResult.getSuccess()) {

        } else {
            wallet = assetsResult.getWallet();
            AssetFundModel hitModel = null;

            if (wallet != null) {
                List<AssetFundModel> list = wallet.getFundModelList();
                setFundName(wallet, lookups);
                for (AssetFundModel m : list) {
                    Boolean isB = product.getChiNameAbbr().replace("A", "B").equals(m.getFundName());
                    if (fundCode.equals(m.getFundCode()) || isB) {
                        hitModel = m;
                        fundCode = m.getFundCode();
                        break;
                    }
                }
                if (hitModel != null) {
                    result.setTodayIncome(MoneyUtils.formatMoney(hitModel.getTotalTodayIncome()));
                    result.setWalletMoney(MoneyUtils.formatMoney(hitModel.getTotalWorthValue()));
                    result.setTotalIncome(MoneyUtils.formatMoney(hitModel.getTotalAccumIncome()));
                    result.setFundName(hitModel.getFundName());
                    result.setFundCode(hitModel.getFundCode());

                }

                //设置是否能买卖
//                if (wallet.getTotalWorthValue() != null) {
//                    if (wallet.getTotalWorthValue().compareTo(GlobalConstant.BIG_DECIMAL_0) > 0
//                        && wallet.getTotalWorthValue().compareTo(wallet.getInTransitAmount()) > 0) {
//                        hasBalance = true;
//                    }
//                }
            }
        }
        product = fundDetailService.getSecurityProduct(fundCode);

        if (product == null) {
            logger.error("Security Product is not exist");
            result.setErrorCode(ErrorConstant.ERROR_FUND_NOT_EXIST);
            result.setErrorMessage(MessageUtil.getErrorMessage(result.getErrorCode(), null));
            result.setSuccess(false);
            return result;
        }
        result.setFundName(product.getChiNameAbbr());
        result.setFundCode(fundCode);
        // 七日年化率
        FundMMYieldPerformance perf = fundMMYieldPerformanceService.getFundMMYieldPerformance(product.getInnerCode());
        result.setNetValueDate(perf.getTradingDay());
        result.setLatestWeeklyYield(MoneyUtils.formatMoneyNoRound(perf.getLatestWeeklyYield(), 2));// 最近7日折算年收益率（%）
        result.setDailyProfit(MoneyUtils.formatMoney(perf.getDailyProfit(), 4));

        // 最近七天的七日年化收益率(降序)
        setWeekYield(product, result);
        setSellAndBuy(hasBalance, fundCode, wallet, result);
        result.setSuccess(true);
        return result;
    }

    @Override
    public int getFastRedeemCountInCurrentDay(Long userId, String brandsType) {
        ValueOperations<String, Integer> op = redisTemplate.opsForValue();

        String currentDate = DateUtil.format(new Date(), "yyyyMMdd");

        String key = CacheUtil.getKey(CacheKeyConst.FAST_REDEEM_COUNT, currentDate, brandsType, userId.toString());

        Integer count = op.get(key);

        logger.info("{}={}", key, count);

        return count != null ? count.intValue() : 0;
    }

    @Override
    public int increaseFastRedeemCountInCurrentDay(Long userId, String brandsType) {
        ValueOperations<String, Integer> op = redisTemplate.opsForValue();

        String currentDate = DateUtil.format(new Date(), "yyyyMMdd");

        String key = CacheUtil.getKey(CacheKeyConst.FAST_REDEEM_COUNT, currentDate, brandsType, userId.toString());

        Integer count = op.get(key);

        logger.info("{}={}", key, count);

        Date now = new Date();
        long detal = DateUtil.getEndTime().getTime() - now.getTime();

        if (count == null) {
            count = 1;
        } else {
            count = count + 1;
        }
        op.set(key, count, detal, TimeUnit.MILLISECONDS);

        return count.intValue();
    }

    @Override
    public BigDecimal getFastRedeemDayUsedMoney(Long userId, String brandsType) {

        String currentDate = DateUtil.format(new Date(), "yyyyMMdd");

        String key = CacheUtil.getKey(CacheKeyConst.FAST_REDEEM_DAY_HAS_USED_MONEY, currentDate, brandsType, userId.toString());

        ValueOperations<String, String> opStr = redisTemplateStr.opsForValue();

        String resultStr = opStr.get(key);

        logger.info("{}={}", key, resultStr);

        BigDecimal value;
        if (StringUtil.isEmpty(resultStr)) {
            value = new BigDecimal("0");
        } else {
            value = new BigDecimal(resultStr);
        }

        return value;
    }

    @Override
    public BigDecimal increaseFastRedeemDayUsedMoney(Long userId, String brandsType, BigDecimal money) {

        String currentDate = DateUtil.format(new Date(), "yyyyMMdd");

        String key = CacheUtil.getKey(CacheKeyConst.FAST_REDEEM_DAY_HAS_USED_MONEY, currentDate, brandsType, userId.toString());

        ValueOperations<String, String> opStr = redisTemplateStr.opsForValue();

        String resultStr = opStr.get(key);

        logger.info("{}={}", key, resultStr);

        Date now = new Date();
        long detal = DateUtil.getEndTime().getTime() - now.getTime();

        BigDecimal totalUsedValue;
        if (StringUtil.isEmpty(resultStr)) {
            totalUsedValue = money;
            opStr.set(key, totalUsedValue.toString(), detal, TimeUnit.MILLISECONDS);
        } else {
            BigDecimal initValue = new BigDecimal(resultStr);
            totalUsedValue = initValue.add(money);
            opStr.set(key, totalUsedValue.toString(), detal, TimeUnit.MILLISECONDS);
        }

        return totalUsedValue;
    }

    //设置每日最高份额
    @Override
    public void setMaxLimit(WalletDetailResult result, List<Lookup> lookups, String fundCode, Boolean isOrg) {
        Lookup hitLookup = null;
        if (!StringUtil.isEmpty(fundCode)) {
            for (Lookup lookup : lookups) {
                // =1 当前在用的T0
                if (fundCode.equals(lookup.getRuleValue())) {
                    logger.info("fundCode={} in T0 List", lookup.getRuleValue());
                    hitLookup = lookup;
                    break;
                }
            }

            if (hitLookup == null || hitLookup.getComponentValue() == null) {
                logger.error("There is no T0 Fund or componentValue, please check!");
                ExceptionHandler.publish(ErrorConst.WALLET_INVALID_FUND_CODE);
            }


//            if (hitLookup.getComponentValue().contains("{")) {
//                result.setMaxLimit(net.sf.json.JSONObject.fromObject(hitLookup.getComponentValue()).optString(!isOrg ? "user_limit" : "org_limit"));
//            } else {
//                result.setMaxLimit(hitLookup.getComponentValue());
//            }
            // spy 2017-11-1
            String str;
            if (FundUtil.isBaoYingT0AB(fundCode)) {
                Lookup lookup = lookupService.getLookup(RuleConstant.T0FUND_BAOYING_CONFIG);
                str = lookup.getComponentValue();
            } else {
                str = hitLookup.getComponentValue();
            }
            String maxLimit = LookupUtil.getT0FieldValueStr(str, isOrg, RuleConstant.FIELD_DAY_LIMIT);
            result.setMaxLimit(maxLimit);

        }
    }

    @Override
    public void setWeekYield(SecurityProduct product, WalletDetailResult result) {
        FundNetValueHistoryResult netValueHistorys = fundDetailService.getFundNetValueHistory(product.getInnerCode(), 1,
            WALLET_NET_VALUE_LIST_DAYS, FundConst.SORT_DESC);

        if (ListUtil.isNotEmpty(netValueHistorys.getNetValueList())) {
            List<WalletDetailRateSimpleModel> latestWeeklyYieldList = new ArrayList<>(WALLET_NET_VALUE_LIST_DAYS);

            for (FundNetValueModel model : netValueHistorys.getNetValueList()) {
                WalletDetailRateSimpleModel newModel = new WalletDetailRateSimpleModel();
                newModel.setRate(model.getLatestWeeklyYield());
                newModel.setTradingDay(model.getTradingDay());
                latestWeeklyYieldList.add(newModel);
            }
            Collections.reverse(latestWeeklyYieldList); // 升序
            result.setLatestWeeklyYieldList(latestWeeklyYieldList);
        }
    }

    @Override
    public void setSellAndBuy(Boolean hasBalance, String fundCode, AssetFundModel wallet, WalletDetailResult result) {

        // Shangdu Lin - 20170502 - 设置基金状态
        FundModel fundModel = queryService.queryFund(fundCode);
        if (fundModel != null && wallet != null) {
            // 零钱;日收益;是否能提现;是否能存入
            result.setCanBuy(fundModel.getCanBuy());
            hasBalance = setHasBalance(fundCode, wallet);
            if (hasBalance) {
                // 是否能提现（不能提现场景：1.未登陆；2.未开户成功；3.当前用户没有持有宝盈货币的份额）
                result.setCanSell(fundModel.getCanSell());
            } else {
                result.setCanSell(false);
            }
        } else {
            // 如果未找到该只基金，则不能买不能卖
            result.setCanBuy(false);
            result.setCanSell(false);
        }
    }

    private boolean setHasBalance(String fundCode, AssetFundModel wallet) {
        List<AssetFundModel> list = wallet.getFundModelList();
        for (AssetFundModel m : list) {
            if (m.getFundCode().equals(fundCode) && m.getEnableShares().compareTo(new BigDecimal(0)) > 0) {
                return true;
            }
        }
        return false;
    }

}
